// Copyright (C) 2012 Yury Bychenkov (http://bychenkov.com).
// All rights reserved.
//
// Redistribution and use in source and binary forms, with or without
// modification, are permitted provided that the following conditions are
// met:
//
//  - Redistributions of source code must retain the above copyright
// notice, this list of conditions and the following disclaimer.
//  - Redistributions in binary form must reproduce the above
// copyright notice, this list of conditions and the following disclaimer
// in the documentation and/or other materials provided with the
// distribution.
//  - Neither the name of Yury Bychenkov nor the names of its
// contributors may be used to endorse or promote products derived from
// this software without specific prior written permission.
//
// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.

#ifndef logchunk_h
#define logchunk_h

#include "common.h"
#include "mappedfile.h"
#include "concurrent.h"
#include "schema.h"
#include "traits.h"

class LogChunk {
public:
	static LogChunk* open(const char* file, bool readOnly);
	static LogChunk* create(const char* file, uint columnCount, uint blockSize, uint blockCapacity);

	bool isOpened() const;
	void close();

	~LogChunk();

	void begin();
	bool add(const void* data, uint dataSize);
	bool commit();

	uint64 estimateColumnSize(uint columnIndex) const;

private:
	#pragma pack(push, 1)
	struct Header {
		uint32 magic;
		uint32 headerSize;
		uint32 blockSize;
		uint32 blockCapacity;
		uint32 bucketCount;
		uint32 reserved[3];

		uint32 bucketTails[1];
	};
	#pragma pack(pop)

	struct Bucket {
		uint8* data;
		uint offset;
		uint size;
		uint tail;
	};

public:
	class Iterator {
	public:
		Iterator();

		uint getValueIndex() const;
		const uint8* getValue() const;
		template <typename ElementTraits> bool skip();
		template <typename ElementTraits> bool skip(uint skipCount);

	private:
		Iterator(const LogChunk& chunk, uint bucketIndex);
		bool skipBlock();

	private:
		const LogChunk* chunk;

		const uint8* value;
		uint valueIndex;

		uint8* data;
		uint offset;
		uint size;
		uint tail;

		friend class LogChunk;
	};

	Iterator iterator(uint bucketIndex) const;


private:
	static const uint32 MAGIC = 0x9ACE;
	static const uint32 BLOCK_HEADER_SIZE = 2 * sizeof(uint32);

	LogChunk(MappedFile* mappedFile);

private:
	MappedFile* mappedFile;
	mutable LatchLock lock;

	uint headerSize;
	Header* header;

	uint blockSize;
	uint blockCount;
	uint blockCapacity;
	uint8* data;

	Bucket* buckets;
	uint bucketCount;

	uint bucketIndex;
	uint32 hashCode;

	static Logger logger;

	friend class Iterator;
};



class LogChunkProvider {
public:
	LogChunkProvider(const Schema& schema_, const LogChunk& chunk_);

	const Schema& getSchema() const;

	bool isIndexAvailable(uint columnIndex) const;
	uint getCardinality(uint columnIndex) const;
	template <typename ElementTraits> int find(uint columnIndex, const uint8* value) const;

	class Cursor {
	public:
		Cursor(const LogChunkProvider& p, const uint* columnMapping, uint columnCount, const uint32*, uint); 
		
		uint getValueIndex() const;
		uint32 getIndex(uint columnIndex);
		
  		const uint8* getString(uint columnIndex);
		const uint8* getDecimal(uint columnIndex);
		const uint8* getInteger(uint columnIndex);

		bool next();

	private:
		uint valueIndex;
		uint firstColumnType;	
		LogChunk::Iterator iterators[MAX_FILTERABLE_COLUMNS];
	};

private:
	const Schema& schema;
	const LogChunk& chunk;
};

#include "logchunk.hpp"

#endif // logchunk_h
